Spring boot之事务

Spring的事务机制

所有的数据库访问技术都有事务处理机制,这些技术提供了API用于开启事务,提交事务完成数据操作,或者在发生错误的时候回滚数据。Spring的事务机制是用统一的机制来处理不同数据访问技术的事务处理,Spirng的事务机制提供了一个PlatformTransactionManager接口,不同的数据访问技术的事务使用不同的接口实现,如下表:

数据访问技术 实现
JDBC DataSourceTransactionManager
JPA JPATransactionManager
Hibernate HibernateTransactionManager
JDO JdoTransactionManager

声明式事务

Spring支持声明式事务,即使用注解来选择需要使用事务的方法,他使用@Transactional注解在方法上表明该方法需要事务支持。备注解的方法在被调用时,Spring开启一个新的事务,当方法无异常运行结束后,Spring会提交这个事务。如:

1
2
3
4
@Transactional
public void saveStudent(Student student){
// 数据库操作
}

注意,@Transactional注解来自于org.springframework.transcation.annotation包,而不是javax.transaction。

注解事务行为

@Transactional有如下表所示的属性来定制事务行为

属性 含义
Propagation 事务传播行为
isolation 事务隔离级别
readOnly 事务的读写性,boolean型
timeout 超时时间,int型,以秒为单位。
rollbackFor 一组异常类,遇到时回滚。(rollbackFor={SQLException.class})
rollbackForClassName 一组异常类名,遇到回滚,类型为 string[]
noRollbackFor 一组异常类,遇到不回滚
norollbackForClassName 一组异常类名,遇到时不回滚。

类级别使用@Transactional

@Transactional不仅可以注解在方法上,还可以注解在类上。注解在类上意味着此类的所有public方法都开启了事务。如果类级别和方法级别同时使用了@Transactional注解,则使用在类级别的注解会重载方法级别的注解(也就是已类级别的注解最终生效)。

Spring Boot的事务支持

  • 自动配置的事务管理器,在使用JDBC作为数据库访问技术时(现在一般很少),配置如下:
1
2
3
4
5
6
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(DataSource.class)
public PlatformTransactionManager transactionManager(){
return new DataSourceTransactionManager(this.dataSource)
}

在使用JAP作为数据持久化技术时,配置如下:

1
2
3
4
5
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public PlatformTransactionManager transactionManager(){
return new JpaTransactionManager()
}

@Bean注解可以参考Springboot注解详解,大意是Springboot启动后扫描的一系列组件的标识

实战

演示如何使用@Transactional使用异常数据回滚与使用异常导致数据不回滚

  • 实体类(Entity)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Student {

@Id
@GeneratedValue
private Integer id;

private String name;

private Integer age;
}
  • 数据持久化层(Dao层)
    这里使用的是JPA
1
2
3
@Repository
public interface StudentRepository extends JpaRepository<Student, Integer> {
}
  • Service层
1
2
3
4
5
6
7
public interface StudentService {

Student saveStudentWithRollBack(Student student);

Student saveStudentWithoutRollBack(Student student);

}
  • Service实现层(Impl)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Service
public class StudentServiceImpl implements StudentService {

@Autowired
// 直接注入 StudentRepository 的 bean
private StudentRepository studentRepository;

// 使用 @Transactional 注解的 rollbackFor 属性,指定特定异常时,触发回滚
@Transactional(rollbackFor = {IllegalArgumentException.class})
@Override
public Student saveStudentWithRollBack(Student student) {
Student s = studentRepository.save(student);
if ("高斯林".equals(s.getName())){
//硬编码,手动触发异常
throw new IllegalArgumentException("高斯林已存在,数据将回滚");
}
return s;
}

// 使用 @Transactional 注解的 noRollbackFor 属性,指定特定异常时,不触发回滚
@Transactional(noRollbackFor = {IllegalArgumentException.class})
@Override
public Student saveStudentWithoutRollBack(Student student) {
Student s = studentRepository.save(student);
if ("高斯林".equals(s.getName())){
throw new IllegalArgumentException("高斯林已存在,数据将不会回滚");
}
return s;
}

}
  • controller层(接口调用层)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RestController
@RequestMapping("/student")
public class StudentController {

// 注入 studentservice 的 bean
@Autowired
private StudentService studentService;

// 测试回滚情况
@PostMapping("/withRollBack")
public Student saveStudentWithRollBack(@RequestBody Student student){
return studentService.saveStudentWithRollBack(student);
}

// 测试不回滚情况
@PostMapping("/withOutRollBack")
public Student saveStudentWithoutRollBack(@RequestBody Student student){
return studentService.saveStudentWithoutRollBack(student);
}
}

测试结果:有异常回滚成功后,数据并没有写入数据库;不回滚,有异常数据也会插入数据库